package com.lucien.Search;

import java.util.Arrays;

public class FibonacciSearch {
    public static int maxsize = 20;
    public static void main(String[] args) {
        int [] arr = {1,8,10,89,1000,1234};

        System.out.println(fibSearch(arr,1234));
    }
    //mid = low + F[k-1]-1,需要使用到斐波那契数列
    //非递归的方法得到一个斐波那契数列
    public static int [] fib(){
        int [] f = new int [maxsize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < maxsize; i++) {
            f[i] = f[i-1]+f[i-2];
        }
        return f;
    }

    /**
     *  编写斐波那契查找算法(使用非递归写算法)
     * @param a   数组
     * @param key 需要查找的关键码(值)
     * @return    返回对应下标，没有则返回-1
     */
    public static int fibSearch (int [] a,int key){
        int low = 0;
        int high= a.length - 1;
        int k =0; //表示斐波那契分割数值的下标
        int mid = 0;//存放中间值
        int [] f = fib();//获得斐波那契数列
        //获取斐波那契分割数值的下标
        while (high > f[k] - 1){
            k++;
        }
        //因为 f[k] 值可能大于a的长度，因此需要使用Arrays类 构造一个新的数组，并指向a[]
        //不足的部分会使用0填充
        int [] temp = Arrays.copyOf(a,f[k]);
        //实际上需求使用a数组最后的数填充temp
        //eg：temp = {1,8,10,89,1000,1234,0,0,0} => {1,8,10,89,1000,1234,1234,1234,1234}
        for (int i = high+1; i < temp.length; i++) {
            temp[i] = a[high];
        }

        //使用while来循环处理，找到我们的数key
        while(low <= high){
            mid = low + f[k-1] -1;
            if (key < temp[mid]){ //需要向数组的前面查找(左边)
                high = mid - 1;
                k--;
                /*  k--
                    1、全部元素 = 前面元素 + 后边元素
                    2、f[k] = f[k-1] + f[k-2];
                    3、因为后面有f[k-2]所以可以继续拆分f[k-1] = f[k-2] + f[k-3];
                    4、即在f[k-1]的前面继续查找 k--;
                    5、即下次循环 mid = f[k-1-1] - 1;
                 */
            }else if (key > temp[mid]){//需要向数组的后面查找(右边)
                low = mid + 1;
                k -=2;
                /*  k -=2
                    1、全部元素 = 前面元素 + 后边元素
                    2、f[k] = f[k-1] + f[k-2];
                    3、因为后面有f[k-2]所以可以继续拆分f[k-1] = f[k-3] + f[k-4];
                    4、即在f[k-2]的前面继续查找 k -=2;
                    5、即下次循环 mid = f[k-1-2] - 1;
                 */
            }else{ //找到
                //需要确定返回的是哪个下标
                if(mid <= high){
                    return mid;
                }else {
                    return high;
                }
            }
        }
            return -1;
    }



}
